 (function() {
   // body...
   var root = this;


   function WApp(wilddogRef, options) {
     var configA = {
       authDomain: "kmxxg-chat.wilddog.com",
       syncURL: "https://kmxxg-chat.wilddogio.com"
     };

     wilddog.initializeApp(configA);
     this._wilddog = wilddog.sync();
     // User-specific instance variables.
     this._user = null;
     this._userId = null;
     this._userName = null;
     this._userAvatar = null;
     this._isModerator = false;

     // A unique id generated for each session.
     this._sessionId = null;

     // A mapping of event IDs to an array of callbacks.
     this._events = {};

     // A mapping of room IDs to a boolean indicating presence.
     this._rooms = {};

     // A mapping of operations to re-queue on disconnect.
     this._presenceBits = {};

     // Commonly-used Wilddog references.
     this._userRef = null;
     this._messageRef = this._wilddog.child('room-messages');
     this._roomRef = this._wilddog.child('room-metadata');
     this._upRef = this._wilddog.child('up-metadata');
     this._privateRoomRef = this._wilddog.child('room-private-metadata');
     this._moderatorsRef = this._wilddog.child('moderators');
     this._suspensionsRef = this._wilddog.child('suspensions');
     this._usersOnlineRef = this._wilddog.child('user-names-online');

     // Setup and establish default options.
     this._options = options || {};

     // The number of historical messages to load per room.
     this._options.numMaxMessages = this._options.numMaxMessages || 50;
   };
   root.WApp = WApp;
   // console.log(wilddogRef)
   // Wildchat Internal Methods
   // --------------
   WApp.prototype = {

     // Load the initial metadata for the user's account and set initial state.
     _loadUserMetadata: function(onComplete) {
       var self = this;

       // Update the user record with a default name on user's first visit.
       this._userRef.transaction(function(current) {
         if (!current || !current.id || !current.name) {
           return {
             id: self._userId,
             name: self._userName,
             avatar: self._userAvatar
           };
         }
       }, function(error, committed, snapshot) {
         self._user = snapshot.val();
         self._moderatorsRef.child(self._userId).once('value', function(snapshot) {
           self._isModerator = !!snapshot.val();
           root.setTimeout(onComplete, 0);
         });
       });
     },

     // Initialize Wilddog listeners and callbacks for the supported bindings.
     _setupDataEvents: function() {
       // Monitor connection state so we can requeue disconnect operations if need be.
       this._wilddog.root().child('.info/connected').on('value', function(snapshot) {
         if (snapshot.val() === true) {
           // We're connected (or reconnected)! Set up our presence state.
           for (var i = 0; i < this._presenceBits; i++) {
             var op = this._presenceBits[i],
               ref = this._wilddog.root().child(op.ref);

             ref.onDisconnect().set(op.offlineValue);
             ref.set(op.onlineValue);
           }
         }
       }, this);

       // Generate a unique session id for the visit.
       var sessionRef = this._userRef.child('sessions').push();
       this._sessionId = sessionRef.key();

       // Register our username in the public user listing.
       var usernameRef = this._usersOnlineRef.child(this._userName.toLowerCase());
       var usernameSessionRef = usernameRef.child(this._sessionId);
       this._queuePresenceOperation(usernameSessionRef, {
         id: this._userId,
         name: this._userName
       }, null);

       // Listen for state changes for the given user.
       this._userRef.on('value', this._onUpdateUser, this);

       // Listen for chat invitations from other users.
       this._userRef.child('invites').on('child_added', this._onWildchatInvite, this);

       // Listen for messages from moderators and adminstrators.
       this._userRef.child('notifications').on('child_added', this._onNotification, this);
     },

     // Append the new callback to our list of event handlers.
     _addEventCallback: function(eventId, callback) {
       this._events[eventId] = this._events[eventId] || [];
       this._events[eventId].push(callback);
     },

     // Retrieve the list of event handlers for a given event id.
     _getEventCallbacks: function(eventId) {
       if (this._events.hasOwnProperty(eventId)) {
         return this._events[eventId];
       }
       return [];
     },

     // Invoke each of the event handlers for a given event id with specified data.
     _invokeEventCallbacks: function(eventId) {
       var args = [],
         callbacks = this._getEventCallbacks(eventId);

       Array.prototype.push.apply(args, arguments);
       args = args.slice(1);

       for (var i = 0; i < callbacks.length; i += 1) {
         callbacks[i].apply(null, args);
       }
     },

     // Keep track of on-disconnect events so they can be requeued if we disconnect the reconnect.
     _queuePresenceOperation: function(ref, onlineValue, offlineValue) {
       ref.onDisconnect().set(offlineValue);
       ref.set(onlineValue);
       this._presenceBits[ref.toString()] = {
         ref: ref,
         onlineValue: onlineValue,
         offlineValue: offlineValue
       };
     },

     // Remove an on-disconnect event from firing upon future disconnect and reconnect.
     _removePresenceOperation: function(ref, value) {
       ref.onDisconnect().cancel();
       ref.set(value);
       delete this._presenceBits[path];
     },

     // Event to monitor current user state.
     _onUpdateUser: function(snapshot) {
       this._user = snapshot.val();
       this._invokeEventCallbacks('user-update', this._user);
     },

     // Event to monitor current auth + user state.
     _onAuthRequired: function() {
       this._invokeEventCallbacks('auth-required');
     },

     // Events to monitor room entry / exit and messages additional / removal.
     _onEnterRoom: function(room) {
       this._invokeEventCallbacks('room-enter', room);
     },
     _onNewMessage: function(roomId, snapshot) {
       var message = snapshot.val();
       message.id = snapshot.key();
       this._invokeEventCallbacks('message-add', roomId, message);
     },
     _onRemoveMessage: function(roomId, snapshot) {
       var messageId = snapshot.key();
       this._invokeEventCallbacks('message-remove', roomId, messageId);
     },
     _onLeaveRoom: function(roomId) {
       this._invokeEventCallbacks('room-exit', roomId);
     },

     // Event to listen for notifications from administrators and moderators.
     _onNotification: function(snapshot) {
       var notification = snapshot.val();
       if (!notification.read) {
         if (notification.notificationType !== 'suspension' || notification.data.suspendedUntil < new Date().getTime()) {
           snapshot.ref().child('read').set(true);
         }
         this._invokeEventCallbacks('notification', notification);
       }
     },

     // Events to monitor chat invitations and invitation replies.
     _onWildchatInvite: function(snapshot) {
       var self = this,
         invite = snapshot.val();

       // Skip invites we've already responded to.
       if (invite.status) {
         return;
       }

       invite.id = invite.id || snapshot.key();
       self.getRoom(invite.roomId, function(room) {
         invite.toRoomName = room.name;
         self._invokeEventCallbacks('room-invite', invite);
       });
     },
     _onWildchatInviteResponse: function(snapshot) {
       var self = this,
         invite = snapshot.val();

       invite.id = invite.id || snapshot.key();
       this._invokeEventCallbacks('room-invite-response', invite);
     }
   };

   // Initialize the library and setup data listeners.
   WApp.prototype.setUser = function(userId, userName, userAvatar, callback) {
     var self = this;
     self._wilddog.app.auth().onAuthStateChanged(function(user) {
       if (user) {
         self._userId = userId.toString();
         self._userName = userName.toString();
         self._userAvatar = userAvatar.toString() || '';
         self._userRef = self._wilddog.child('users').child(self._userId);
         self._loadUserMetadata(function() {
           root.setTimeout(function() {
             callback(self._user);
             self._setupDataEvents();
           }, 0);
         });
       } else {
         self.warn('Wildchat requires an authenticated Wilddog reference. Pass an authenticated reference before loading.');
       }
     });
   };


   WApp.prototype.getInitSnap = function() {

   }

   // Miscellaneous helper methods.
   WApp.prototype.getRoom = function(roomId, callback) {
     this._roomRef.child(roomId).once('value', function(snapshot) {

       callback(snapshot.val());
     });
   };
   // 发送一条消息
   WApp.prototype.sendMessage = function(roomId, messageContent, messageType, cb) {
     var self = this,
       message = {
         userId: self._userId,
         name: self._userName,
         timestamp: self._wilddog.ServerValue.TIMESTAMP,
         message: messageContent,
         type: messageType || 'default',
         avatar: self._userAvatar
       },
       newMessageRef;

     if (!self._user) {
       self._onAuthRequired();
       if (cb) {
         cb(new Error('Not authenticated or user not set!'));
       }
       return;
     }

     newMessageRef = self._messageRef.child(roomId).push();
     newMessageRef.setWithPriority(message, self._wilddog.ServerValue.TIMESTAMP, cb);
   };
   // 进入一个房间
   WApp.prototype.enterRoom = function(roomId) {
     var self = this;
     self.getRoom(roomId, function(room) {
       var roomName = room.name;

       if (!roomId || !roomName) return;

       // Skip if we're already in this room.
       if (self._rooms[roomId]) {
         return;
       }

       self._rooms[roomId] = true;

       if (self._user) {
         // Save entering this room to resume the session again later.
         // self._userRef.child('rooms').child(roomId).set({
         //   id: roomId,
         //   name: roomName,
         //   active: true
         // });

         // Set presence bit for the room and queue it for removal on disconnect.
         var presenceRef = self._wilddog.child('room-users').child(roomId).child(self._userId).child(self._sessionId);
         self._queuePresenceOperation(presenceRef, {
           id: self._userId,
           name: self._userName
         }, null);
       }

       // Invoke our callbacks before we start listening for new messages.
       self._onEnterRoom({
         id: roomId,
         name: roomName
       });

       // Setup message listeners
       self._roomRef.child(roomId).once('value', function(snapshot) {
         self._messageRef.child(roomId).limitToLast(self._options.numMaxMessages).on('child_added', function(snapshot) {
           self._onNewMessage(roomId, snapshot);
         }, /* onCancel */ function() {
           // Turns out we don't have permission to access these messages.
           self.leaveRoom(roomId);
         }, /* context */ self);

         self._messageRef.child(roomId).limitToLast(self._options.numMaxMessages).on('child_removed', function(snapshot) {
           self._onRemoveMessage(roomId, snapshot);
         }, /* onCancel */ function() {}, /* context */ self);
       }, /* onFailure */ function() {}, self);
     });
   };
   // Create and automatically enter a new chat room.
   WApp.prototype.createRoom = function(roomName, roomType, callback) {
     var self = this,
       newRoomRef = this._roomRef.push();

     var newRoom = {
       id: newRoomRef.key(),
       name: roomName,
       type: roomType || 'public',
       createdByUserId: this._userId,
       createdAt: this._wilddog.ServerValue.TIMESTAMP
     };

     if (roomType === 'private') {
       newRoom.authorizedUsers = {};
       newRoom.authorizedUsers[this._userId] = true;
     }

     newRoomRef.set(newRoom, function(error) {
       if (!error) {
         self.enterRoom(newRoomRef.key());
       }
       if (callback) {
         callback(newRoomRef.key());
       }
     });
   };
   WApp.prototype.getInitMessage = function(roomId, callback) {
     var self = this;
     roomId && self._messageRef.child(roomId).once('value').then(
       function(data) {
         if (callback) {
           callback(data.val());
         }
       }).catch(function() {
       if (callback) {
         callback(new Error('Not authenticated or user not set!'));
       }
     })
   }
   WApp.prototype.getInitUp = function(upId, callback) {
     var self = this;
     self._upRef=self._upRef.child(upId);
     upId && self._upRef.once('value').then(
       function(snapshot) {
           self.currentCount = snapshot.val().count;
           callback&& callback(self.currentCount);

           self._upRef.on('child_changed', function(snapshot,prev) {
 
              callback&&  callback(self.currentCount);
         })
       }

     ).catch(function(err) {
       callback(new Error(err));
     })

   }
   WApp.prototype.coomitUp = function( callback) {
       var self = this;
       // console.log(self.currentCount)
       self.currentCount++ && self._upRef.update({"count":self.currentCount}).then(
         function() {

           callback&&callback();

         }
       ).catch(function() {
         callback&&callback(new Error('服务器错误'));
       })
     }
     // Callback registration. Supports each of the following events:
   WApp.prototype.on = function(eventType, cb) {
     this._addEventCallback(eventType, cb);
   };

 })();
